#ifndef ELEMENT_H
#define ELEMENT_H

#include <QObject>
#include <QRect>
#include <QVector>
#include <functional>
#include "qlui_global.h"
#include "paintdevice.h"

class QXmlStreamReader;
class Context;
class WindowManager;

class QLUISHARED_EXPORT Element : public QObject,  protected PaintDevice
{
    Q_OBJECT
public:
    Element(Element *parent=NULL);
    virtual ~Element();

    /*********************************************************************
     *                    Copying
     * *******************************************************************/
public:
    virtual bool copyFrom(const Element *e);
    virtual Element* clone() const;
    
    /*********************************************************************
     *                    Factoary
     * ******************************************************************/
public:
    static Element* createElementByType(const QString &name);

    enum SkipType {
        SkipAfterLoaded,
        StayAfterLoaded
    };

    virtual bool loadXML(QXmlStreamReader &root, Context *context, SkipType skip=SkipAfterLoaded);

    /*********************************************************************
     *                    WindowManager
     * ******************************************************************/
public:
    void setWindowManager(WindowManager *wm);
    WindowManager* windowManager() const;

private:
    WindowManager *wm_;
    
    /*********************************************************************
     *                    Position & Size
     * *******************************************************************/
public:
    Q_PROPERTY(int x READ x WRITE setX NOTIFY appearanceChanged)
    Q_PROPERTY(int y READ y WRITE setY NOTIFY appearanceChanged)
    Q_PROPERTY(int width READ width WRITE setWidth NOTIFY appearanceChanged)
    Q_PROPERTY(int height READ height WRITE setHeight NOTIFY appearanceChanged)

    void setPosition(int x, int y);
    void setX(int x);
    int x() const;
    void setY(int y);
    int y() const;

    void setSize(int w, int h);
    void setWidth(int w);
    int width() const;

    /**
     * @brief setHeight
     * @param h
     *
     * @warning 在未设置其他位置的情况下先设置height有可能造成位置错误。
     */
    void setHeight(int h);
    int height() const;

    void setRect(const QRect &rect);
    QRect rect() const;

    virtual QRect sizeHint() const;

signals:
    void appearanceChanged();

protected:
    QRect rect_;

    /*********************************************************************
     *                    visiable
     * *******************************************************************/
public:
    Q_PROPERTY(bool visiable READ isShow WRITE setVisiable NOTIFY visiableChanged)

    void hide();
    void show();
    void setVisiable(bool visiable);
    bool isShow() const;
    bool isHide() const;
    bool isVisiable() const;

public slots:
    bool update();

protected:
    /**
     * @brief execute before update. if return false, stop update.
     * @return
     */
    virtual bool prevUpdate();

signals:
    void visiableChanged(bool visiable);

private:
    bool hide_;

    /*********************************************************************
     *                    paint
     * *******************************************************************/
public:
    Q_PROPERTY(quint32 color READ color WRITE setColor)
    Q_PROPERTY(quint32 background READ background WRITE setBackground)

    void setColor(quint32 color);
    quint32 color() const;

    void setBackground(quint32 color);
    quint32 background() const;

protected:

    virtual void paintEvent(){}
    virtual void hideEvent();

private:
    quint32 font_color_;
    quint32 back_color_;

    /*********************************************************************
     *                    name & id
     * *******************************************************************/
public:
    Q_PROPERTY(quint32 id READ id WRITE setID)
    Q_PROPERTY(QString name READ name WRITE setName)
    Q_PROPERTY(QString type READ type)
    Q_PROPERTY(QString path READ path WRITE setPath)

    static quint32 invalidID();
    void setID(quint32 id);
    quint32 id() const;
    void setName(const QString& name);
    QString name() const;
    void setPath(const QString &path);
    QString path() const;
    virtual QString type() const;

private:
    quint32 id_;
    QString name_;
    QString path_;

    /*********************************************************************
     *                    Event
     * *******************************************************************/
public:
    Q_PROPERTY(int key READ bindingKey WRITE bindKey)
    Q_PROPERTY(QList<int> keys READ bindingKeys WRITE bindKeys)
    Q_PROPERTY(bool needFocus READ needFocus WRITE setNeedFocus)
    Q_PROPERTY(bool enable READ enable WRITE setEnable)

    void bindKeys(QList<int> keys);
    QList<int> bindingKeys() const;

    void bindKey(int key);
    int bindingKey() const;
    bool hasBindKey(int key);

    void setNeedFocus(bool needFocus);
    bool needFocus() const;
    void setFocus(bool focus);
    bool hasFocus();

    void setEnable(bool enable);
    bool enable() const;

    void setModified(bool m);
    bool modified();

    typedef std::function<bool(int, int)> KeyEventFilter;
    void setKeyEventFilter(const KeyEventFilter &filter);

    /**
     * @brief create keyboard event, if pass throught filter, emit keyEvent(key, value)
     * @param key
     * @param value
     */
    void keyboardEvent(int key, int value);

signals:
    void moveEvent(MoveDirection direction);

signals:
    void keyEvent(int key, int value);

private:
    QList<int> keys_;
    bool need_focus_;
    bool has_focus_;
    bool enable_;
    bool modified_;
    KeyEventFilter filter_;

    /*********************************************************************
     *                    MetaInfo
     * *******************************************************************/
public:
    virtual QString getSignalSignature(const QString& name) const;

protected:

};

class RegisterElementType
{
public:
    RegisterElementType(const QString &name, const Element &e);
};


#endif // ELEMENT_H
